home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / mknames.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  9.9 KB  |  424 lines  |  [TEXT/R*ch]

  1. /* Name generation for Xconq.
  2.    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* Naming is a special class of init method, since it may run both during
  10.    init and throughout a game.  Name generation has a very strong influence
  11.    on the flavor of a game, so it has some extra flexibility, including
  12.    a capability to use a simple context-free grammar to generate names. */
  13.  
  14. #include "conq.h"
  15.  
  16. int totalsideweights;
  17.  
  18. void
  19. init_namers()
  20. {
  21.     totalsideweights = 0;
  22. }
  23.  
  24. /* The default side name list has the form
  25.    (((name "A") (noun "Aian")) ... ). */
  26.  
  27. void
  28. set_g_side_lib_default()
  29. {
  30.     int i;
  31.     Obj *tmp;
  32.     Obj *sidenamelist = lispnil, *sidenamelistend = lispnil;
  33.  
  34.     for (i = 0; i < MAXSIDES; ++i) {
  35.     sprintf(spbuf, "%c", 'A' + i);
  36.     sprintf(tmpbuf, "%cian", 'A' + i); /* should be in nlang.c? */
  37.     tmp = lispnil;
  38.     tmp = cons(cons(intern_symbol(keyword_name(K_NAME)),
  39.             cons(new_string(copy_string(spbuf)), lispnil)),
  40.            tmp);
  41.     tmp = cons(cons(intern_symbol(keyword_name(K_NOUN)),
  42.             cons(new_string(copy_string(tmpbuf)), lispnil)),
  43.            tmp);
  44.     tmp = cons(tmp, lispnil);
  45.     if (sidenamelist == lispnil) {
  46.         sidenamelist = tmp;
  47.     } else {
  48.         set_cdr(sidenamelistend, tmp);
  49.     }
  50.     sidenamelistend = tmp;
  51.     }
  52.     set_g_side_lib(sidenamelist);
  53. }
  54.  
  55. /* Pick a side name that is not already being used. */
  56.  
  57. void
  58. make_up_side_name(side)
  59. Side *side;
  60. {
  61.     int uniq = FALSE, tries = 0, n, sofar;
  62.     Obj *sidelib, *subobj, *subelts, *lis, *filler;
  63.  
  64.     if ((sidelib = g_side_lib()) == lispnil)
  65.       return;
  66.     /* (what if all weights were explicitly 0?) */
  67.     if (totalsideweights == 0) {
  68.     for (lis = sidelib; lis != lispnil; lis = cdr(lis)) {
  69.         totalsideweights +=
  70.           (numberp(car(car(lis))) ? c_number(car(car(lis))) : 1);
  71.     }
  72.     }
  73.     filler = lispnil;
  74.     while (tries++ < 100 * numsides) {
  75.     n = xrandom(totalsideweights);
  76.     sofar = 0;
  77.     subobj = lispnil;
  78.     for (lis = sidelib; lis != lispnil; lis = cdr(lis)) {
  79.         sofar += (numberp(car(car(lis))) ? c_number(car(car(lis))) : 1);
  80.         if (sofar > n) {
  81.         subobj = car(lis);
  82.         break;
  83.         }
  84.     }
  85.     /* (should scan and preprocess subobj before using it) */
  86.     /* Remove the weighting if present. */
  87.     if (numberp(car(subobj))) {
  88.         subobj = cdr(subobj);
  89.     }
  90.     uniq = TRUE;
  91.     for (subelts = subobj; subelts != lispnil; subelts = cdr(subelts)) {
  92.         if (stringp(cadr(car(subelts)))) {
  93.         if (name_in_use(side, c_string(cadr(car(subelts))))) {
  94.             uniq = FALSE;
  95.             break;
  96.         }
  97.         }
  98.     }
  99.     if (uniq) {
  100.         filler = subobj;
  101.         break;
  102.     }
  103.     }
  104.     /* Now fill the side from the chosen obj - no effect if it is nil. */
  105.     fill_in_side(side, filler, FALSE);
  106. }
  107.  
  108. /* This tests whether a given string is already being used by a side. */
  109.  
  110. int
  111. name_in_use(side, str)
  112. Side *side;
  113. char *str;
  114. {
  115.     Side *side2;
  116.  
  117.     if (empty_string(str))
  118.       return FALSE;
  119.     for_all_sides(side2) {
  120.     if (side2 != side) {
  121.         if ((side2->name && strcmp(str, side2->name) == 0)
  122.         || (side2->noun && strcmp(str, side2->noun) == 0)
  123.         || (side2->pluralnoun && strcmp(str, side2->pluralnoun) == 0)
  124.         || (side2->adjective && strcmp(str, side2->adjective) == 0)
  125.         )
  126.           return TRUE;
  127.     }
  128.     }
  129.     return FALSE;
  130. }
  131.  
  132. Obj *
  133. make_namer(sym, meth)
  134. Obj *sym, *meth;
  135. {
  136.     Obj *namer = new_pointer(sym, (char *) meth);
  137.  
  138.     return namer;
  139. }
  140.  
  141. /* Method to add names to units that want them and don't have them already. */
  142.  
  143. int
  144. name_units_randomly(calls, runs)
  145. int calls, runs;
  146. {
  147.     Unit *unit;
  148.  
  149.     /* There's never any reason not to run this method. */
  150.     /* (should this announce progress?) */
  151.     for_all_units(unit) {
  152.     make_up_unit_name(unit);
  153.     assign_unit_number(unit);
  154.     }
  155.     return TRUE;
  156. }
  157.  
  158. /* Given a unit, return its naming method if it has one. */
  159.  
  160. char *
  161. unit_namer(unit)
  162. Unit *unit;
  163. {
  164.     Side *side;
  165.  
  166.     if (unit == NULL)
  167.       return NULL;
  168.     /* Look for and return a side-specific namer if found. */
  169.     side = (unit->side ? unit->side : indepside);
  170.     if (side->unitnamers != NULL && side->unitnamers[unit->type] != NULL) {
  171.         return side->unitnamers[unit->type];
  172.     }
  173.     return u_namer(unit->type);
  174. }
  175.  
  176. /* Generate a name for a unit, using an appropriate method.
  177.  
  178.    It is possible (in fact encouraged) to add cool new unit name generation
  179.    methods in here, especially when the grammar-based or thematic methods
  180.    don't give the desired results. */
  181.  
  182. char *
  183. propose_unit_name(unit)
  184. Unit *unit;
  185. {
  186.     int u;
  187.     char *method;
  188.  
  189.     if (unit == NULL)
  190.       return NULL;
  191.     u = unit->type;
  192.     method = unit_namer(unit);
  193.     if (empty_string(method)) {
  194.     /* Nothing to work with. */
  195.     } else if (boundp(intern_symbol(method))) {
  196.     return run_namer(symbol_value(intern_symbol(method)));
  197.     } else {
  198.     /* Do builtin naming methods. */
  199.     switch (keyword_code(method)) {
  200.       case K_JUNKY:
  201.         /* Kind of a bizarre thing, but flavorful sometimes. */
  202.         if (unit->side) {
  203.         sprintf(spbuf, "%c%c-%s-%02ld",
  204.             uppercase(unit->side->name[0]),
  205.             uppercase(unit->side->name[1]),
  206.             utype_name_n(u, 3), unit->number);
  207.         } else {
  208.         sprintf(spbuf, "%s-%d", utype_name_n(u, 3), unit->id);
  209.         }
  210.         return copy_string(spbuf);
  211.       default:
  212.         init_warning("No naming method `%s', ignoring", method);
  213.         break;
  214.     }
  215.     }
  216.     return NULL;
  217. }
  218.  
  219. /* This names only units that do not already have names. */
  220.  
  221. void
  222. make_up_unit_name(unit)
  223. Unit *unit;
  224. {
  225.     if (unit == NULL || unit->name != NULL)
  226.       return;
  227.     /* (should check that proposed name is not in use by matching side and type?) */
  228.     unit->name = propose_unit_name(unit);
  229. }
  230.  
  231. /* Unit numbering only happens to designated types that are on a side. */
  232.  
  233. void
  234. assign_unit_number(unit)
  235. Unit *unit;
  236. {
  237.     if (u_assign_number(unit->type)
  238.     && unit->side != NULL
  239.     && unit->number == 0) {
  240.     /* Give it the next available number and increment. */
  241.     unit->number = (unit->side->counts)[unit->type]++;
  242.     } else {
  243.     /* Note that this will erase any already-assigned number,
  244.        if the type is one that is not supposed to be numbered. */
  245.     unit->number = 0;
  246.     }
  247. }
  248.  
  249. /* Given a naming method, run it and get back a string. */
  250.  
  251. char *
  252. run_namer(namer)
  253. Obj *namer;
  254. {
  255.     int len, ix;
  256.     Obj *prev;
  257.     Obj *rslt;
  258.     Obj *code = (Obj *) namer->v.ptr.data;
  259.     Obj *type;
  260.  
  261.     if (!consp(code))
  262.       return "?format?";
  263.     type = car(code);
  264.     if (!symbolp(type))
  265.       return "?type?";
  266.     switch (keyword_code(c_string(type))) {
  267.       case K_JUNKY:
  268.       case K_RANDOM:
  269.         len = length(cdr(code));
  270.     if (len > 0) {
  271.         ix = xrandom(len - 1) + 1;
  272.         prev = code;
  273.         while (--ix)
  274.           prev = cdr(prev);
  275.         rslt = cadr(prev);
  276.         /* Splice out our desired name. */
  277.         set_cdr(prev, cddr(prev));
  278.         return c_string(rslt);
  279.     } else {
  280.         return "?no more names?";
  281.     }
  282.     break;
  283.       case K_GRAMMAR:
  284.     return name_from_grammar(code);
  285.       default:
  286.     return "?method?";
  287.     }
  288. }
  289.  
  290. static int maxdepth;
  291.  
  292. char *
  293. name_from_grammar(grammar)
  294. Obj *grammar;
  295. {
  296.     char rslt[500];  /* not really safe... */
  297.     Obj *root = cadr(grammar);
  298.     Obj *depth = caddr(grammar);
  299.     Obj *rules = cdr(cddr(grammar));
  300.  
  301.     maxdepth = 5;
  302.     if (numberp(depth))
  303.       maxdepth = c_number(depth);
  304.     rslt[0] = '\0';
  305.     gen_name(root, rules, 0, rslt);
  306.     /* This should be optional maybe. */
  307.     rslt[0] = uppercase(rslt[0]);
  308.     return copy_string(rslt);
  309. }
  310.  
  311. /* Given a nonterminal and a set of rules, find and apply the right rule. */
  312.  
  313. void
  314. gen_name(nonterm, rules, depth, rslt)
  315. Obj *nonterm, *rules;
  316. int depth;
  317. char *rslt;
  318. {
  319.     Obj *lis;
  320.  
  321.     for (lis = rules; lis != lispnil; lis = cdr(lis)) {
  322.     if (equal(nonterm, car(car(lis)))) {
  323.         gen_from_rule(cadr(car(lis)), rules, depth, rslt);
  324.         return;
  325.     }
  326.     }
  327.     if (symbolp(nonterm)
  328.     && boundp(nonterm)
  329.     && pointerp(symbol_value(nonterm))) {
  330.     strcat(rslt, run_namer(symbol_value(nonterm)));
  331.     } else {
  332.     /* Assume that the purported nonterm symbol is actually a terminal. */
  333.     strcat(rslt, c_string(nonterm));
  334.     }
  335. }
  336.  
  337. /* Given a rule body, decide how to add to the output string.  This may
  338.    recurse, so there is a limit check. */
  339.  
  340. void
  341. gen_from_rule(rule, rules, depth, rslt)
  342. Obj *rule, *rules;
  343. int depth;
  344. char *rslt;
  345. {
  346.     Obj *lis;
  347.     int total, num, oldlen;
  348.     
  349.     if (depth >= maxdepth)
  350.       return;
  351.     switch (rule->type) {
  352.       case NUMBER:
  353.       case NIL:
  354.       case UTYPE:
  355.       case MTYPE:
  356.       case TTYPE:
  357.       case POINTER:
  358.       case EOFOBJ:
  359.     break;  /* ignore for now.. */
  360.       case SYMBOL:
  361.     gen_name(rule, rules, depth, rslt);
  362.     break;
  363.       case STRING:
  364.     strcat(rslt, c_string(rule));
  365.     break;
  366.       case CONS:
  367.         if (symbolp(car(rule))) {
  368.         switch (keyword_code(c_string(car(rule)))) {
  369.           case K_OR:
  370.         /* weighted selection */
  371.         total = 0;
  372.         for (lis = cdr(rule); lis != lispnil; lis = cdr(lis)) {
  373.             if (numberp(car(lis))) {
  374.             total += c_number(car(lis));
  375.             lis = cdr(lis);
  376.             } else {
  377.             total += 1;
  378.             }
  379.         }
  380.         /* We now know the range, make a random index into it. */
  381.         num = xrandom(total);
  382.         /* Go through again to figure out which choice the index
  383.            references. */
  384.         total = 0;
  385.         for (lis = cdr(rule); lis != lispnil; lis = cdr(lis)) {
  386.             if (numberp(car(lis))) {
  387.             total += c_number(car(lis));
  388.             lis = cdr(lis);
  389.             } else {
  390.             total += 1;
  391.             }
  392.             if (total > num) {
  393.             gen_from_rule(car(lis), rules, depth + 1, rslt);
  394.             return;
  395.             }
  396.         }
  397.         break;
  398.           case K_ANY:
  399.         /* uniform selection */
  400.         strcat(rslt, c_string(elt(cdr(rule),
  401.                       xrandom(length(cdr(rule))))));
  402.         break;
  403.           case K_CAPITALIZE:
  404.         oldlen = strlen(rslt);
  405.         gen_name(cadr(rule), rules, depth + 1, rslt);
  406.         if (islower(rslt[oldlen]))
  407.           rslt[oldlen] = uppercase(rslt[oldlen]);
  408.         break;
  409.           default:
  410.         /* Nested subsequence. */
  411.         for (lis = rule; lis != lispnil; lis = cdr(lis)) {
  412.             gen_from_rule(car(lis), rules, depth + 1, rslt);
  413.         }
  414.         }
  415.     } else {
  416.         /* syntax error */
  417.     }
  418.     break;
  419.       default:
  420.     case_panic("lisp type in grammar", rule->type);
  421.     break;
  422.     }
  423. }
  424.